#ifndef INTERCEPTION_H
#define INTERCEPTION_H

#include "asan_internal_defs.h"

/**
 * Note: the following macros are borrowed from LLVM
 * In this way, we could improve the portability via changing the macro for
 * different platforms. We currently merely support linux for aarch64
 * Also, the following macros contain declarition for interceptors, we
 * reuse those macros as the interception mechanism is the same as that
 * of LLVM ASAN (using dlsym)
 * **/

#define SANITIZER_LINUX 1

// These typedefs should be used only in the interceptor definitions to replace
// the standard system types (e.g. SSIZE_T instead of ssize_t)
typedef __sanitizer::uptr    SIZE_T;
typedef __sanitizer::sptr    SSIZE_T;
typedef __sanitizer::sptr    PTRDIFF_T;
typedef __sanitizer::s64     INTMAX_T;
typedef __sanitizer::u64     UINTMAX_T;
typedef __sanitizer::OFF_T   OFF_T;
typedef __sanitizer::OFF64_T OFF64_T;

# define WRAP(x) __interceptor_ ## x
# define WRAPPER_NAME(x) "__interceptor_" #x
# define INTERCEPTOR_ATTRIBUTE __attribute__((visibility("default")))
# define DECLARE_WRAPPER(ret_type, func, ...) \
    extern "C" ret_type func(__VA_ARGS__) \
    __attribute__((weak, alias("__interceptor_" #func), visibility("default")));


# define PTR_TO_REAL(x) real_##x
# define REAL(x) __sanitizer::PTR_TO_REAL(x)
# define FUNC_TYPE(x) x##_type

# define DECLARE_REAL(ret_type, func, ...) \
    typedef ret_type (*FUNC_TYPE(func))(__VA_ARGS__); \
    namespace __sanitizer { \
      extern FUNC_TYPE(func) PTR_TO_REAL(func); \
    }
# define ASSIGN_REAL(dst, src) REAL(dst) = REAL(src)

# define DECLARE_REAL_AND_INTERCEPTOR(ret_type, func, ...)  \
    DECLARE_REAL(ret_type, func, __VA_ARGS__)               \
    extern "C" ret_type WRAP(func)(__VA_ARGS__);
// Declare an interceptor and its wrapper defined in a different translation
// unit (ex. asm).
# define DECLARE_EXTERN_INTERCEPTOR_AND_WRAPPER(ret_type, func, ...)    \
  extern "C" ret_type WRAP(func)(__VA_ARGS__); \
  extern "C" ret_type func(__VA_ARGS__);


// Generally, you don't need to use DEFINE_REAL by itself, as INTERCEPTOR
// macros does its job. In exceptional cases you may need to call REAL(foo)
// without defining INTERCEPTOR(..., foo, ...). For example, if you override
// foo with an interceptor for other function.
# define DEFINE_REAL(ret_type, func, ...)            \
    typedef ret_type (*FUNC_TYPE(func))(__VA_ARGS__); \
    namespace __sanitizer {                        \
    FUNC_TYPE(func) PTR_TO_REAL(func);                \
    }

#define INTERCEPTOR(ret_type, func, ...) \
  DEFINE_REAL(ret_type, func, __VA_ARGS__) \
  DECLARE_WRAPPER(ret_type, func, __VA_ARGS__) \
  extern "C" \
  INTERCEPTOR_ATTRIBUTE \
  ret_type WRAP(func)(__VA_ARGS__)

// We don't need INTERCEPTOR_WITH_SUFFIX on non-Darwin for now.
#define INTERCEPTOR_WITH_SUFFIX(ret_type, func, ...) \
  INTERCEPTOR(ret_type, func, __VA_ARGS__)

#define INCLUDED_FROM_INTERCEPTION_LIB

#if SANITIZER_LINUX
# include "interception_linux.h"
# define INTERCEPT_FUNCTION(func) INTERCEPT_FUNCTION_LINUX_OR_FREEBSD(func)
# define INTERCEPT_FUNCTION_WITH_ADDR(func, addr) INTERCEPT_FUNCTION_LINUX_OR_FREEBSD_WITH_ADDR(func, addr)

# define INTERCEPT_FUNCTION_VER(func, symver) \
    INTERCEPT_FUNCTION_VER_LINUX_OR_FREEBSD(func, symver)
#endif

#undef INCLUDED_FROM_INTERCEPTION_LIB

#endif  // INTERCEPTION_H
